# 在高德地图上显示指定目录下的所有照片拍摄位置
# xiaoyf 编写
# pyinstaller -F d:\pythonp\wx2\view_jpgs_on_gdmap.py
# pyinstaller -F view_jpgs_on_gdmap.py

import wx
import wx.html2
import os
import subprocess
import exiftool
import json
# 导入高德地图配置参数 api_key
import gd_config
import requests
import re
import time
import random

# # 定义一组坐标
# points = [
#     [116.397428, 39.984245],
#     [116.398428, 39.985245],
#     [116.399428, 39.986245],
#     [116.400428, 39.987245],
#     [116.401428, 39.988245]
# ]
#
# # 定义中心点坐标
# center_point = points[0]


class MyFrameJpgsOnMap(wx.Frame):
    def __init__(self):
        super(MyFrameJpgsOnMap, self).__init__(None, title="在高德地图上显示照片拍摄位置", size=(1000, 700))

        self.panel = wx.Panel(self)

        # 创建一个水平布局管理器
        hbox = wx.BoxSizer(wx.HORIZONTAL)

        # 录入框
        self.directory_input = wx.TextCtrl(self.panel, style=wx.TE_READONLY, size=(400, -1))

        # 目录选择按钮
        self.select_button = wx.Button(self.panel, label="选择目录")
        self.select_button.Bind(wx.EVT_BUTTON, self.on_select_directory)

        # 退出按钮
        self.exit_button = wx.Button(self.panel, label="退出")
        self.exit_button.Bind(wx.EVT_BUTTON, self.on_exit)

        # 将控件添加到水平布局中
        hbox.Add(self.directory_input, 1, wx.EXPAND | wx.ALL, 5)
        hbox.Add(self.select_button, 0, wx.ALL, 5)
        hbox.Add(self.exit_button, 0, wx.ALL, 5)

        # 创建一个垂直布局管理器
        vbox = wx.BoxSizer(wx.VERTICAL)
        vbox.Add(hbox, 0, wx.EXPAND)  # 添加水平布局到垂直布局
        self.web_view = wx.html2.WebView.New(self.panel)
        vbox.Add(self.web_view, 1, wx.EXPAND)

        self.panel.SetSizer(vbox)

        # 加载地图
        #self.load_map()

        self.Show()

    def on_select_directory(self, event):
        with wx.DirDialog(self, "选择一个目录", style=wx.DD_DEFAULT_STYLE) as dir_dialog:
            if dir_dialog.ShowModal() == wx.ID_OK:
                directory_path = dir_dialog.GetPath()
                self.directory_input.SetValue(directory_path)

        sorted_list = sorted(os.listdir(directory_path))
        # 定义一组坐标
        points = []
        # 定义标题数组
        titles = []
        num = 1
        for index,file in enumerate(sorted_list):
            exiftool_city_string = ''
            city_string = ''
            if file.lower().endswith(('.jpg', '.jpeg', '.arw', '.nef', '.cr2', '.cr3','.rw2','.dng','.heic')):
                file_path = os.path.join(directory_path, file)
                gps_info = self.get_exif_info(file_path)
                if gps_info == None:
                    #print('没有GPS信息！')
                    city_string = ''
                else:
                    latitude =  gps_info[0]
                    longitude = gps_info[1]
                    time_original = gps_info[2]
                    make_string = gps_info[3]
                    make_string2 = gps_info[4]
                    #print(latitude,longitude)
                    # 通过exiftool获取城市名称
                    exiftool_city_string = self.get_exiftool_cityinfo(file_path)
                    #转换位高德坐标系
                    gdgpsinfo = self.gps_to_gdmap(longitude, latitude)
                    if gdgpsinfo != None:
                        # 使用split方法分割字符串
                        longitude, latitude = gdgpsinfo[0].split(',')
                        city_string = gdgpsinfo[1]
                        # 检查是否有重复的坐标，有加个随机数
                        for i,point in enumerate(points):
                            if point[0] == longitude and point[1] == latitude :
                                # 将经纬度坐标转换为数字类型
                                lng = float(longitude)
                                lat = float(latitude)

                                # 通过随机数对经纬度坐标做调整
                                lng += (-0.0001 if i % 2 == 0 else 0.0001) * random.random()
                                lat+= (-0.0001 if i % 2 == 0 else 0.0001) * random.random()

                                # 将经纬度坐标转换为字符型
                                longitude = str(lng)
                                latitude = str(lat)
                                break

                        points.append([longitude, latitude])
                        file_string = f'{num},文件名：{file} <br /> 拍摄时间：{time_original} <br /> 设备：{make_string2}'
                        titles.append(file_string)

                        print()
                        print(f'{num},{file_path}')
                        print(make_string)
                        print(city_string)
                        print(f' {exiftool_city_string}')
                        num = num + 1
            # 等待 20 秒
            time.sleep(0.2)

        # # 定义中心点坐标
        if not points:  # 或者使用 if len(points) == 0:
            print('该目录下的照片文件没有gps信息！')
            wx.MessageBox("该目录下的照片文件没有gps信息！", "错误", wx.OK | wx.ICON_ERROR)
        else:
            # print(len(points),len(titles))
            #print(points)
            center_point = points[0]
            # 加载地图
            self.load_map(points,center_point,titles)

    def gps_to_gdmap(self,longitude, latitude):
        # 转换为 GCJ-02 坐标
        gd_appkey = gd_config.gd_api_key
        url = f'https://restapi.amap.com/v3/assistant/coordinate/convert?key={gd_appkey}&locations={longitude},{latitude}&coordsys=gps&output=json'
        # print(url)
        response = requests.get(url)
        # print(response)
        if response.status_code == 200:
            data = json.loads(response.text)
            if data['status'] == '1':
                # 转换后的高德坐标
                # {"status":"1","info":"ok","infocode":"10000","locations":"117.163480631511,34.228663194445"}
                amap_location = data['locations']  # [0]
                # 117.163480631511,34.228663194445
                # print(f"高德坐标: {amap_location}")

                # 根据参考方向调整坐标值
                # if gps_latitude_ref == "S":
                # latitude = -latitude
                # if gps_longitude_ref == "W":
                # longitude = -longitude

                # 使用高德地图API将 GPS 坐标转换为城市名称
                url = f"https://restapi.amap.com/v3/geocode/regeo?output=json&location={amap_location}&key={gd_appkey}&radius=1000&extensions=all"
                response = requests.get(url)
                data = response.json()

                # 提取城市名称
                result = ''
                if data.get("status") == "1" and "regeocode" in data:
                    address_component = data["regeocode"].get("addressComponent", {})
                    city = address_component.get("city", "")
                    province = address_component.get("province", "")
                    district = address_component.get("district", "")
                    country = address_component.get("country", "")
                    township = address_component.get("township", "")
                    street = address_component.get("streetNumber", {}).get("street", "")

                    if city:
                        # 添加拍摄位置到结果中
                        result = f" 拍摄于: {country}, {province}, {city}, {district}, {street}, {township} "
                        #print(result)
                return amap_location,result
            else:
                return None
        else:
            return None

    def get_exiftool_cityinfo(self, image_file_path):
        # 要处理的图像文件路径
        # 构建 ExifTool 命令
        command = ['exiftool', '-api', 'geolocation', '-geolocation*', image_file_path]

        # 执行命令并捕获输出
        try:
            result = subprocess.run(command, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
            # print("ExifTool 输出：")
            # print(result.stdout)  # 输出结果
            output = result.stdout
            # 使用正则表达式提取每一行的值
            geolocation_data = {}
            for line in output.splitlines():
                match = re.match(r'(.+?):\s*(.+)', line)  # 匹配 "键 : 值" 格式
                if match:
                    key = match.group(1).strip()
                    value = match.group(2).strip()
                    geolocation_data[key] = value

            # 打印提取的值
            # print("\n提取的地理位置信息：")
            citylist = []
            for key, value in geolocation_data.items():
                # print(f"{key}: {value}")
                citylist.append(value)
            city_string = f'国家：{citylist[4]},省：{citylist[1]},市：{citylist[2]},城市：{citylist[0]}'
            # print(city_string)
            return city_string
        except subprocess.CalledProcessError as e:
            print(f"执行命令时出错: {e.stderr}")  # 捕获并打印错误信息
        except Exception as e:
            print(f"发生其他错误: {e}")
        return ''

    def get_exif_info(self, file_path):
        try:
            # 取完整的 EXIF 信息
            #result = subprocess.check_output(["exiftool", file_path], universal_newlines=True)

            # 字段名替换字典
            field_replacements = {
                "Media Create Date": "\n媒体建立时间",
                "Date/Time Original": "\n拍摄时间",
                "File Modification Date/Time": "\n文件修改时间",
                "Lens ID": "镜头型号",
                "Lens Make": "镜头制造商",
                "Android Make": "手机制造商",
                "Make ": "设备制造商 ",
                "Camera Model Name": "设备型号",
                "Lens Model": "镜头型号",
                "Lens Type": "镜头类型"
            }

            # 创建 ExifTool 对象
            x1 = '1'  # 假设没有 GPS 信息
            with exiftool.ExifToolHelper() as et:
                metadata_list = et.get_metadata(file_path)
                if isinstance(metadata_list, list):
                    exif_make = ''
                    exif_model = ''
                    for metadata in metadata_list:
                        if 'EXIF:Make' in metadata:
                            exif_make = metadata['EXIF:Make']
                        if 'EXIF:Model' in metadata:
                            exif_model = metadata['EXIF:Model']
                        make_string = f' {exif_make} {exif_model} '
                        make_string2 = make_string
                        if 'EXIF:UniqueCameraModel' in metadata:
                            exif_uniquecameramodel = metadata['EXIF:UniqueCameraModel']
                            make_string = make_string + f' {exif_uniquecameramodel} '
                            make_string2 = make_string
                        #print(make_string ,end='')

                        #print(metadata)
                        time_original = metadata.get("EXIF:DateTimeOriginal")
                        OffsetTime = metadata.get("EXIF:OffsetTime")
                        OffsetTimeOriginal = metadata.get("EXIF:OffsetTimeOriginal")
                        FileModifyDate = os.path.getmtime(file_path)

                        err,gps_latitude, gps_longitude, gps_latitude_ref, gps_longitude_ref = self.extract_gps(metadata,file_path)
                        if err == '0':
                            x1 = '1'
                            break  # 找到第一个有效的 GPS 信息就退出
                        if gps_latitude and gps_longitude:
                            x1 = '0'
                            break  # 找到第一个有效的 GPS 信息就退出

            # 如果有 GPS 信息，处理坐标
            gpsif = x1
            if x1 == '0':

                make_string = make_string + f'拍摄日期：{time_original}'
                #print(f'拍摄日期：{time_original}', end='')
                latitude = self.convert_to_degrees2(gps_latitude)
                longitude = self.convert_to_degrees2(gps_longitude)

                # 根据参考方向调整坐标值
                if gps_latitude_ref == "S":
                    latitude = -latitude
                if gps_longitude_ref == "W":
                    longitude = -longitude
                make_string = make_string + f' 坐标：{longitude},{latitude}'
                make_string2 =  make_string2 + f'<br /> 坐标：{longitude},{latitude}'
                #print(f' 坐标：{longitude},{latitude}')
                return latitude, longitude,time_original,make_string,make_string2
            else:
                #latitude = ''
                #longitude = ''
                return None
            # 设置 EXIF 文本框的值
            #print(result)
        except subprocess.CalledProcessError as e:
            # 捕获异常并设置错误信息
            print(f"获取 EXIF 信息时出错: {e}")
            return None
        except Exception as ex:
            # 捕获其他异常
            print(f"发生错误: {ex}")
            return None

    # 将 GPS 坐标转换为浮点数
    def convert_to_degrees2(self, value):
        # 检查输入的值是否为浮点数或整数
        if isinstance(value, (float, int)):
            return value  # 直接返回浮点数或整数
        # 检查输入的值是否为字符串
        elif isinstance(value, str):
            # 从字符串中解析出度、分、秒
            parts = value.split(' ')
            d = float(parts[0])  # 解析度
            m = float(parts[1])  # 解析分
            s = float(parts[2])  # 解析秒
            # 将度、分、秒转换为浮点数表示的度
            return d + (m / 60.0) + (s / 3600.0)
        # 检查输入的值是否为列表
        elif isinstance(value, list):
            d, m, s = value  # 解包列表中的度、分、秒
            # 将度、分、秒转换为浮点数表示的度
            return d + (m / 60.0) + (s / 3600.0)
        else:
            # 如果输入值的格式不正确，抛出异常
            raise ValueError("无法解析的 GPS 坐标格式")

    # 提取 GPS 信息的函数
    def extract_gps(self, metadata, file_path):
        # print(metadata)
        if "EXIF:GPSLatitude" in metadata:
            return (
                '1',
                metadata.get("EXIF:GPSLatitude"),
                metadata.get("EXIF:GPSLongitude"),
                metadata.get("EXIF:GPSLatitudeRef"),
                metadata.get("EXIF:GPSLongitudeRef")
            )
        else:
            return '0', '', '', '', ''

    def on_exit(self, event):
        self.Close()

    def load_map(self,points,center_point,titles):
        # 将坐标转换为字符串
        points_str = "[[" + "],[".join(str(point)[1:-1] for point in points) + "]]"
        # 将标题数组转换为字符串
        titles_str = "["
        for title in titles:
            titles_str += f"'{title}',"
        titles_str = titles_str[:-1] + "]"

        center_str = str(center_point)[1:-1]
        gd_appkey = gd_config.gd_api_key

        # offset: new AMap.Pixel(-10, -34)

        self.html = f"""
                    <html>
                        <head>
                            <script type="text/javascript" src="https://webapi.amap.com/maps?v=1.4.15&key={gd_appkey}"></script>
                        </head>
                        <body>
                            <div id="map" style="width: 100%; height: 100%;"></div>
                            <script>
                                const map = new AMap.Map("map", {{
                                    center: [{center_point[0]}, {center_point[1]}],
                                    zoom: 13
                                }});
                                const points = {points_str};
                                const titles = {titles_str};
                                for (let i = 0; i < points.length; i++) {{
                                    const marker = new AMap.Marker({{
                                        "position": new AMap.LngLat(points[i][0], points[i][1]),
                                        icon: 'https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png',                                       
                                        offset: new AMap.Pixel(-10, -34)
                                    }});
                                    marker.on('click', function() {{
                                        const infoWindow = new AMap.InfoWindow({{
                                            content: "<div>" + titles[i] + "</div>",
                                            offset: new AMap.Pixel(0, -30)
                                        }});
                                        infoWindow.open(map, marker.getPosition());
                                    }});
                                    map.add(marker);
                                }}
                            </script>
                        </body>
                    </html>
                """

        # print(self.html)
        self.web_view.SetPage(self.html, "")


if __name__ == "__main__":
    app = wx.App()
    frame = MyFrameJpgsOnMap()
    app.MainLoop()
